package com.nulldev.util.web.HttpServer3.extensions.sse.event;

import java.io.Serializable;

public abstract class SSEvent implements Serializable {

	private static final long serialVersionUID = 5642453586141359781L;

	public abstract static class Builder {
		public abstract Builder id(final String id);

		public abstract Builder event(final String event);

		public abstract Builder data(final String data);

		public abstract Builder retry(final Integer retryMs);

		abstract SSEvent autoBuild();

		public SSEvent build() {
			SSEvent sse = autoBuild();

			String event = sse.getEvent();
			if (event != null && event.isEmpty()) {
				throw new IllegalStateException("Event can't be empty");
			}

			Integer retry = sse.getRetry();
			if (retry != null && retry <= 0) {
				throw new IllegalStateException("Retry must be >0 ms");
			}

			return sse;
		}
	}

	private static class BuilderImpl extends Builder {

		private String id, event, data;
		private Integer retryMs = 2000;

		@Override
		public Builder id(final String id) {
			this.id = id;
			return this;
		}

		@Override
		public Builder event(final String event) {
			this.event = event;
			return this;
		}

		@Override
		public Builder data(final String data) {
			this.data = data;
			return this;
		}

		@Override
		public Builder retry(final Integer retryMs) {
			this.retryMs = retryMs;
			return this;
		}

		@Override
		SSEvent autoBuild() {
			return new SSEvent() {
				private static final long serialVersionUID = -273888961804271881L;

				@Override
				Builder toBuilder() {
					return new BuilderImpl();
				}

				@Override
				public Integer getRetry() {
					return BuilderImpl.this.retryMs;
				}

				@Override
				public String getID() {
					return BuilderImpl.this.id;
				}

				@Override
				public String getEvent() {
					return BuilderImpl.this.event;
				}

				@Override
				public String getData() {
					return BuilderImpl.this.data;
				}
			};
		}

	}

	public static Builder bldr() {
		return new BuilderImpl().event("message");
	}

	public static SSEvent newSSEvent(final String id, final String event, final String data, final Integer retryMs) {
		return bldr().id(id).event(event).data(data).retry(retryMs).build();
	}

	SSEvent() {
	}

	/**
	 * Id of the event. Available as 'lastEventId' on the client
	 * 
	 * @return
	 */
	public abstract String getID();

	/**
	 * Type of the event. Default is 'message'
	 * 
	 * @return
	 */
	public abstract String getEvent();

	/**
	 * Payload of the event
	 * 
	 * @return
	 */
	public abstract String getData();

	/**
	 * Delay in 'ms' before retrying connection after it was closed / broken
	 * 
	 * @return
	 */
	public abstract Integer getRetry();

	/**
	 * Builder instance initialized with values from this event
	 * 
	 * @return
	 */
	abstract Builder toBuilder();

	/**
	 * Exact copy of this instance. To customize it, use {@link #toBuilder()}
	 * 
	 * @return
	 */
	public SSEvent copy() {
		return toBuilder().build();
	}
}